﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace DecodeInterface
{
    public abstract class DecodeBitmap : IDecode<Bitmap, Bitmap>
    {
        protected static Random random = new Random();

        Stopwatch watch = new Stopwatch();

        DecodeExplorer decodeExplorer = null;

        private void CheckInOut(Bitmap imageIn)
        {
            if (imageIn == null)
                throw new ApplicationException("input image is null!|Decode");
        }

        private Size CheckState(object[] state)
        {
            if (state == null)
                throw new ArgumentException("state is null|Decode");
            if (state.Length < 2 || !(state[0] is int) || !(state[1] is int))
                throw new ArgumentException("state must be width and height|Decode");

            return new Size((int)state[0], (int)state[1]);
        }

        #region IDecode<Bitmap,Bitmap> 成员
        Bitmap decodeImage;
        public Bitmap DecodeImage
        {
            get
            {
                if (decodeImage != null)
                {
                    lock (decodeImage)
                    {
                        return decodeImage.Clone() as Bitmap;
                    }
                }
                else
                    return null;
            }
        }

        public Control DecodeShow
        {
            get
            {
                if (decodeExplorer == null) return null;
                else return decodeExplorer.DecodeShow;
            }
            set
            {
                if (decodeExplorer == null && value != null)
                {
                    this.decodeExplorer = new DecodeExplorer(value);
                }
            }
        }

        public long ElapsedMilliseconds
        {
            get { return watch.ElapsedMilliseconds; }
        }

        public object[] Decode(Bitmap imageIn, params object[] state)
        {
            CheckInOut(imageIn);
            Size sizeOut = CheckState(state);
            if (this.decodeImage == null)
                this.decodeImage = new Bitmap(sizeOut.Width, sizeOut.Height);
            Bitmap imageOut = this.decodeImage;

            object[] result = null;
            watch.Reset();
            watch.Start();

            lock (imageIn)
            {
                lock (imageOut)
                {
                    Rectangle rectIn = new Rectangle(0, 0, imageIn.Width, imageIn.Height);
                    Rectangle rectOut = new Rectangle(0, 0, imageOut.Width, imageOut.Height);
                    BitmapData dataIn = imageIn.LockBits(rectIn, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                    BitmapData dataOut = imageOut.LockBits(rectOut, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                    byte[][] bIn = new byte[imageIn.Size.Height][];
                    byte[][] bOut = new byte[imageOut.Size.Height][];

                    unsafe
                    {
                        byte* pIn = (byte*)(dataIn.Scan0.ToPointer());
                        byte* pOut = (byte*)(dataOut.Scan0.ToPointer());
                        result = OnDecode(pIn, pOut, imageIn.Size, imageOut.Size, state);

                        for (int i = 0; i < bIn.GetLength(0); i++)
                        {
                            bIn[i] = new byte[dataIn.Stride];
                            byte* pbIn = &pIn[i * dataIn.Stride];
                            Marshal.Copy((IntPtr)pbIn, bIn[i], 0, dataIn.Stride);
                        }

                        for (int i = 0; i < bOut.GetLength(0); i++)
                        {
                            bOut[i] = new byte[dataOut.Stride];
                            byte* pbOut = &pOut[i * dataOut.Stride];
                            Marshal.Copy((IntPtr)pbOut, bOut[i], 0, dataOut.Stride);
                        }

                        object[] r = OnDecode(bIn, bOut, state);
                        if (r.Length > 0) result = r;

                        for (int i = 0; i < bIn.GetLength(0); i++)
                        {
                            byte* pbIn = &pIn[i * dataIn.Stride];
                            Marshal.Copy(bIn[i], 0, (IntPtr)pbIn, dataIn.Stride);
                        }

                        for (int i = 0; i < bOut.GetLength(0); i++)
                        {
                            byte* pbOut = &pOut[i * dataOut.Stride];
                            Marshal.Copy(bOut[i], 0, (IntPtr)pbOut, dataOut.Stride);
                        }
                    }


                    imageOut.UnlockBits(dataOut);
                    imageIn.UnlockBits(dataIn);
                }
            }

            if (this.decodeExplorer != null)
                this.decodeExplorer.InputImage(this.DecodeImage);

            watch.Stop();

            return result;
        }

        public void ShowImage()
        {
            if (this.decodeExplorer != null)
                this.decodeExplorer.ShowImage();
        }

        #endregion

        unsafe protected abstract object[] OnDecode(byte* pIn, byte* pOut, Size sizeIn, Size sizeOut, params object[] state);

        protected virtual object[] OnDecode(byte[][] bIn, byte[][] bOut, params object[] state)
        {
            return new object[0];
        }
    }
}
